include config.mk

ifeq ($(OS),Windows_NT)
EXE := .exe
else
EXE :=
endif

MODERN := 1

#tools
SCANINC := tools/scaninc/scaninc$(EXE)
PREPROC := tools/preproc/preproc$(EXE)
AIF := tools/aif2pcm/aif2pcm$(EXE)
GFX := tools/gbagfx/gbagfx$(EXE)
FIX := tools/gbafix/gbafix$(EXE)
WAV := tools/wav2agb/wav2agb$(EXE)
MID := tools/mid2agb/mid2agb$(EXE)

#compiler
PREFIX := arm-none-eabi-
OBJCOPY := $(PREFIX)objcopy
READELF := $(PREFIX)readelf
AS := $(PREFIX)as
CC := $(PREFIX)gcc
LD := $(PREFIX)ld
CPP := $(PREFIX)cpp

TOOLCHAIN := $(DEVKITARM)
ifneq (,$(TOOLCHAIN))
  ifneq ($(wildcard $(TOOLCHAIN)/bin),)
    export PATH := $(TOOLCHAIN)/bin:$(PATH)
  endif
endif
MODERNCC := $(PREFIX)gcc
PATH_MODERNCC := PATH="$(PATH)" $(MODERNCC)
CC1 = $(shell $(PATH_MODERNCC) --print-prog-name=cc1) -quiet

#dirs
C_SUBDIR = src
DATA_ASM_SUBDIR = data
ASM_SUBDIR = asm
SOUND_SUBDIR := sound
SONG_SUBDIR = $(SOUND_SUBDIR)/songs
MID_SUBDIR = $(SOUND_SUBDIR)/midi
CRY_SUBDIR := $(SOUND_SUBDIR)/direct_sound_samples/cries

OBJ_DIR := build

C_BUILDDIR = $(OBJ_DIR)/$(C_SUBDIR)
DATA_ASM_BUILDDIR = $(OBJ_DIR)/$(DATA_ASM_SUBDIR)
ASM_BUILDDIR = $(OBJ_DIR)/$(ASM_SUBDIR)
SONG_BUILDDIR = $(OBJ_DIR)/$(SONG_SUBDIR)
MID_BUILDDIR = $(OBJ_DIR)/$(MID_SUBDIR)
CRY_BUILDDIR = $(OBJ_DIR)/$(CRY_SUBDIR)

SHELL := /bin/bash -o pipefail
MID_CFG_PATH := $(MID_SUBDIR)/midi.cfg

#flags
CFLAGS := -mthumb -mno-thumb-interwork -mcpu=arm7tdmi -mtune=arm7tdmi -mno-long-calls -march=armv4t -O2 -fira-loop-pressure -fipa-pta
ASFLAGS := -mthumb -mcpu=arm7tdmi
CPPFLAGS := -iquote include -Wno-trigraphs -DMODERN=$(MODERN)
LDFLAGS := $(LDFILE) -T linker.ld -Map $(MAP)

#objs
C_SRCS_IN := $(wildcard $(C_SUBDIR)/*.c $(C_SUBDIR)/*/*.c $(C_SUBDIR)/*/*/*.c)
C_SRCS := $(foreach src,$(C_SRCS_IN),$(if $(findstring .inc.c,$(src)),,$(src)))
C_OBJS := $(patsubst $(C_SUBDIR)/%.c,$(C_BUILDDIR)/%.o,$(C_SRCS))
C_ASM_SRCS := $(wildcard $(C_SUBDIR)/*.s $(C_SUBDIR)/*/*.s $(C_SUBDIR)/*/*/*.s)
C_ASM_OBJS := $(patsubst $(C_SUBDIR)/%.s,$(C_BUILDDIR)/%.o,$(C_ASM_SRCS))
# 获取所有 data/*.s 文件,除了那些有特定规则的文件
REGULAR_DATA_ASM_SRCS := $(filter-out $(DATA_ASM_SUBDIR)/maps.s $(DATA_ASM_SUBDIR)/map_events.s, $(wildcard $(DATA_ASM_SUBDIR)/*.s))
DATA_ASM_SRCS := $(wildcard $(DATA_ASM_SUBDIR)/*.s)
DATA_ASM_OBJS := $(patsubst $(DATA_ASM_SUBDIR)/%.s,$(DATA_ASM_BUILDDIR)/%.o,$(DATA_ASM_SRCS))
ASM_SRCS := $(wildcard $(ASM_SUBDIR)/*.s)
ASM_OBJS := $(patsubst $(ASM_SUBDIR)/%.s,$(ASM_BUILDDIR)/%.o,$(ASM_SRCS))
SONG_SRCS := $(wildcard $(SONG_SUBDIR)/*.s)
SONG_OBJS := $(patsubst $(SONG_SUBDIR)/%.s,$(SONG_BUILDDIR)/%.o,$(SONG_SRCS))
MID_SRCS := $(wildcard $(MID_SUBDIR)/*.mid)
MID_OBJS := $(patsubst $(MID_SUBDIR)/%.mid,$(MID_BUILDDIR)/%.o,$(MID_SRCS))
CRY_SRCS := $(wildcard $(CRY_SUBDIR)/*.aif)
CRY_OBJS := $(patsubst $(CRY_SUBDIR)/%.aif,$(CRY_BUILDDIR)/%.bin,$(CRY_SRCS))

INCLUDE_DIRS := include
INCLUDE_CPP_ARGS := $(INCLUDE_DIRS:%=-iquote %)
INCLUDE_SCANINC_ARGS := $(INCLUDE_DIRS:%=-I %)

OBJS := $(C_OBJS) $(C_ASM_OBJS) $(ASM_OBJS) $(DATA_ASM_OBJS) $(SONG_OBJS) $(MID_OBJS) $(CRY_OBJS)

SUBDIRS := $(sort $(dir $(OBJS)))
$(shell mkdir -p $(SUBDIRS))

include graphics_file_rules.mk

define MID_RULE
$(MID_SUBDIR)/$1.s: $(MID_SUBDIR)/$1.mid $(MID_CFG_PATH) $(MID)
	$(MID) $$< $$@ $2
endef

define MID_EXPANSION
	$(eval $(call MID_RULE,$(basename $(patsubst %:,%,$(word 1,$1))),$(wordlist 2,999,$1)))
endef
$(foreach line,$(shell cat $(MID_CFG_PATH) | sed "s/ /__SPACE__/g"),$(call MID_EXPANSION,$(subst __SPACE__, ,$(line))))

.DEFAULT_GOAL := all

.DELETE_ON_ERROR:

RULES_NO_SCAN += cleantools clean cleanall syms tools
.PHONY: all
.PHONY: $(RULES_NO_SCAN)

NODEP ?= 0
# Check if we need to pre-build tools and generate assets based on the chosen rule.
ifneq (,$(MAKECMDGOALS))
  ifeq (,$(filter-out $(RULES_NO_SCAN),$(MAKECMDGOALS)))
    NODEP := 1
  endif
endif

# 让 all 目标依赖于生成的 .inc 文件
all: $(GENERATED_INC_FILES) $(OBJS) $(FIX)
	@scripts/syn.py $(SRCROM) $(TARGETROM) $(OFFSET)
	@$(LD) $(LDFLAGS) -o $(OBJ_DIR)/linker.o $(OBJS)
	@$(READELF) -Ws $(OBJ_DIR)/linker.o | tail -n +4 | awk '$$2 ~ /^0(2|3|8|9)/ && $$3 !~ /(Type|FILE|SECTION)/ && $$8 !~ /(\$$|\.)/ {print $$2,$$8}' | sort -k1,1 > $(SYM)
	@$(OBJCOPY) -O binary $(OBJ_DIR)/linker.o $(OBJ_DIR)/output.bin
	@scripts/insert.py $(OFFSET) $(TARGETROM) $(SYM) $(OBJ_DIR)/output.bin
	@$(FIX) $(TARGETROM)

$(OBJ_DIR)/linker.o: $(OBJS)
	@$(LD) $(LDFLAGS) -o $(OBJ_DIR)/linker.o $(OBJS)
	@$(OBJCOPY) -O binary $(OBJ_DIR)/linker.o $(OBJ_DIR)/output.bin

clean:
	@rm -rf $(OBJ_DIR)
	@rm -f $(MAP) $(SYM) $(TARGETROM) $(GENERATED_INC_FILES)

%.s: ;
%.png: ;
%.pal: ;
%.aif: ;

%.1bpp: %.png $(GFX) ; $(GFX) $< $@
%.4bpp: %.png $(GFX) ; $(GFX) $< $@
%.8bpp: %.png $(GFX) ; $(GFX) $< $@
%.gbapal: %.pal $(GFX) ; $(GFX) $< $@
%.gbapal: %.png $(GFX) ; $(GFX) $< $@
%.lz: % $(GFX) ; $(GFX) $< $@
%.rl: % $(GFX) ; $(GFX) $< $@

$(ASM_BUILDDIR)/%.o: $(ASM_SUBDIR)/%.s
	$(AS) $(ASFLAGS) -o $@ $<

$(ASM_BUILDDIR)/%.d: $(ASM_SUBDIR)/%.s
	$(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) $<

ifneq ($(NODEP),1)
-include $(addprefix $(OBJ_DIR)/,$(ASM_SRCS:.s=.d))
endif

$(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.c $(PREPROC)
	$(CPP) $(CPPFLAGS) $< | $(PREPROC) $< charmap.txt -i | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ -

$(C_BUILDDIR)/%.d: $(C_SUBDIR)/%.c $(SCANINC)
	$(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) $<

ifneq ($(NODEP),1)
-include $(addprefix $(OBJ_DIR)/,$(C_SRCS:.c=.d))
endif

$(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.s
	$(PREPROC) $< charmap.txt | $(CPP) $(INCLUDE_SCANINC_ARGS) - | $(PREPROC) -ie $< charmap.txt | $(AS) $(ASFLAGS) -o $@

$(C_BUILDDIR)/%.d: $(C_SUBDIR)/%.s
	$(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I "" $<

ifneq ($(NODEP),1)
-include $(addprefix $(OBJ_DIR)/,$(C_ASM_SRCS:.s=.d))
endif

# 让 data 汇编文件依赖生成的 .inc 文件
$(DATA_ASM_BUILDDIR)/%.o: $(DATA_ASM_SUBDIR)/%.s $(GENERATED_INC_FILES)
	$(PREPROC) $< charmap.txt | $(CPP) $(INCLUDE_SCANINC_ARGS) - | $(PREPROC) -ie $< charmap.txt | $(AS) $(ASFLAGS) -o $@

$(DATA_ASM_BUILDDIR)/%.d: $(DATA_ASM_SUBDIR)/%.s
	$(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I "" $<

ifneq ($(NODEP),1)
-include $(addprefix $(OBJ_DIR)/,$(REGULAR_DATA_ASM_SRCS:.s=.d))
endif

$(MID_BUILDDIR)/%.o: $(MID_SUBDIR)/%.s
	$(AS) $(ASFLAGS) -I $(SOUND_SUBDIR) -o $@ $<

$(SONG_BUILDDIR)/%.o: $(SONG_SUBDIR)/%.s
	$(AS) $(ASFLAGS) -I $(SOUND_SUBDIR) -o $@ $<

$(CRY_BUILDDIR)/%.bin: $(CRY_SUBDIR)/%.aif $(AIF)
	$(AIF) $< $@ --compress

$(CRY_BUILDDIR)/uncomp_%.bin: $(CRY_SUBDIR)/uncomp_%.aif $(AIF) 
	$(AIF) $< $@

$(SOUND_SUBDIR)/%.bin: $(SOUND_SUBDIR)/%.aif $(AIF)
	$(AIF) $< $@

$(MID_SUBDIR)/%.s: $(MID_SUBDIR)/%.mid
	$(warning $< does not have an associated entry in midi.cfg! It cannot be built)

tools: $(SCANINC) $(PREPROC) $(AIF) $(GFX) $(FIX) $(WAV) $(MID)

$(SCANINC):
	@$(MAKE) -C tools/scaninc --silent

$(PREPROC):
	@$(MAKE) -C tools/preproc --silent

$(AIF):
	@$(MAKE) -C tools/aif2pcm --silent

$(GFX):
	@$(MAKE) -C tools/gbagfx --silent

$(FIX):
	@$(MAKE) -C tools/gbafix --silent

$(WAV):
	@$(MAKE) -C tools/wav2agb --silent

$(MID):
	@$(MAKE) -C tools/mid2agb --silent

cleantools:
	@$(MAKE) -C tools/scaninc clean --silent
	@$(MAKE) -C tools/preproc clean --silent
	@$(MAKE) -C tools/aif2pcm clean --silent
	@$(MAKE) -C tools/gbagfx clean --silent
	@$(MAKE) -C tools/wav2agb clean --silent
	@$(MAKE) -C tools/mid2agb clean --silent
	@$(MAKE) -C tools/gbafix clean --silent

cleanall: clean
	@find . \( -iname '*.1bpp' -o -iname '*.4bpp' -o -iname '*.8bpp' -o -iname '*.rl' -o -iname '*.latfont' -o -iname '*.hwjpnfont' -o -iname '*.fwjpnfont' -o -iname '*.4bpp.lz' -o -iname '*.gbapal' -o -iname '*.bin.lz' -o -iname '*.map' \) -exec rm {} +

syms: $(OBJ_DIR)/linker.o
	@$(READELF) -Ws $< | tail -n +4 | awk '$$2 ~ /^0(2|3|8|9)/ && $$3 !~ /(Type|FILE|SECTION)/ && $$8 !~ /(\$$|\.)/ {print $$2,$$8}' | sort -k1,1 > $@
